package network

import (
	"gomir/mlog"
	"reflect"
)

func EncodeProtocol(p Protocol) (b []byte) {
	defer func() {
		if err := recover(); err != nil {
			mlog.Panic(err)
			b = nil
		}
	}()

	w := NewBytesWriter()
	encode(p, w)
	b = w.Bytes()
	return
}

func encode(v interface{}, w *BytesWriter) {
	rv := reflect.ValueOf(v)
	if rv.Type().Kind() == reflect.Ptr {
		rv = rv.Elem()
	}
	encodeValue(rv, w)
}

func encodeValue(rv reflect.Value, w *BytesWriter) {
	if rv.Kind() == reflect.Ptr {
		if rv.IsNil() { //服务端协议有指针类型，但此处不应有空指针
			panic("marshal ptr is nil")
		}
		rv = rv.Elem()
	}
	switch rv.Kind() {
	case reflect.Bool:
		w.WriteBool(rv.Interface().(bool))
	case reflect.Int8:
		w.WriteInt8(rv.Interface().(int8))
	case reflect.Uint8:
		switch v := rv.Interface().(type) {
		case uint8:
			w.WriteUint8(v)
		default: //针对common中的uint8枚举常量类型
			w.WriteUint8(uint8(reflect.ValueOf(v).Uint()))
		}
	case reflect.Int16:
		w.WriteInt16(rv.Interface().(int16))
	case reflect.Uint16:
		switch v := rv.Interface().(type) {
		case uint16:
			w.WriteUint16(v)
		default: //针对common中的uint16枚举常量类型
			w.WriteUint16(uint16(reflect.ValueOf(v).Uint()))
		}
	case reflect.Int32, reflect.Int:
		w.WriteInt32(rv.Interface().(int32))
	case reflect.Uint32, reflect.Uint:
		w.WriteUint32(rv.Interface().(uint32))
	case reflect.Int64:
		w.WriteInt64(rv.Interface().(int64))
	case reflect.Uint64:
		w.WriteUint64(rv.Interface().(uint64))
	case reflect.Float32:
		w.WriteFloat32(rv.Interface().(float32))
	case reflect.Float64:
		w.WriteFloat64(rv.Interface().(float64))
	case reflect.String:
		w.WriteString(rv.String())
	case reflect.Array:
		for i := 0; i < rv.Len(); i++ {
			encodeValue(rv.Index(i), w)
		}
	case reflect.Slice:
		l := rv.Len()
		w.WriteUint32(uint32(l))
		switch rv.Type().Elem().Kind() {
		case reflect.Int8, reflect.Uint8:
			w.WriteBytes(rv.Bytes())
		default:
			for i := 0; i < l; i++ {
				encodeValue(rv.Index(i), w)
			}
		}
	case reflect.Struct:
		for i := 0; i < rv.NumField(); i++ {
			f := rv.Type().Field(i)
			fv := rv.Field(i)
			tag := f.Tag.Get("codec")
			if tag == "-" {
				continue
			}
			if tag == "ptr" { //主要针对*cm.UserItem
				if fv.Kind() != reflect.Ptr {
					panic("marshal error codec ptr")
				}
				if fv.IsNil() {
					w.WriteBool(false)
				} else {
					w.WriteBool(true)
					encodeValue(fv, w)
				}
			} else if tag == "ptrslice" { //主要针对[]*cm.UserItem
				if fv.Kind() != reflect.Slice || fv.Type().Elem().Kind() != reflect.Ptr {
					panic("marshal error codec ptrslice")
				}
				if fv.IsNil() || fv.Len() == 0 {
					w.WriteBool(false)
				} else {
					w.WriteBool(true)
					l := fv.Len()
					w.WriteUint32(uint32(l))
					for i := 0; i < l; i++ {
						fvi := fv.Index(i)
						if fvi.IsNil() {
							w.WriteBool(false)
						} else {
							w.WriteBool(true)
							encodeValue(fvi, w)
						}
					}
				}
			} else {
				encodeValue(fv, w)
			}
		}
	default:
		panic("marshal unkown type")
	}
}
